package cn.main.utils;

import org.jetbrains.annotations.NotNull;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


/**
 * Created by cheng on 2016/5/14.
 */
public class DES {


    /**
     * 3DES加解密
     *
     * @author steven-pan
     */

    private static final String ALGORITHM_MD5 = "md5";

    private static final String ALGORITHM_DESEDE = "DESede";

    private static final String CIPHER_TRANSFORMATION = "DESede/CBC/PKCS5Padding";

    private static final String CHARSET_UTF_8 = "UTF-8";

    /**
     * test
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        System.out.println("DESede加解密测试：");


        String text = "helloworld!";// origin data
        System.out.println("加密前:" + text);
        String codedtext = encrypt(text);

//        String codedtextb = encode(codedtext);// data transfer as text
        System.out.println("Base64 format:" + codedtext);
//        codedtext = decode(codedtextb);

        String decodedtext = decrypt(codedtext);
        System.out.println("解密后:" + decodedtext); // This correctly shows "hello world!"
    }

    private static final String privateKey = "zfd;[8a3GY8&93j_@$$%(*HF";

    /**
     * encoded message
     *
     * @param message origin message
     * @param sKey    origin privateKey
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(String message, String sKey) {

        try {
            byte[] keyBytes = getKeyBytes(sKey);
            SecretKey key = new SecretKeySpec(keyBytes, ALGORITHM_DESEDE);
            IvParameterSpec iv = new IvParameterSpec(new byte[8]);
            Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, key, iv);
            byte[] plainTextBytes = message.getBytes(CHARSET_UTF_8);
            byte[] cipherText = cipher.doFinal(plainTextBytes);
            return cipherText;
        } catch (Exception e) {
            e.printStackTrace();
            return new byte[0];
        }
    }

    /**
     * decode from encoded message
     *
     * @param message encoded message
     * @param sKey    origin privateKey
     * @return
     * @throws Exception
     */
    public static String decrypt(byte[] message, String sKey) {
        try {


        final byte[] keyBytes = getKeyBytes(sKey);

        final SecretKey key = new SecretKeySpec(keyBytes, ALGORITHM_DESEDE);
        final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
        final Cipher decipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
        decipher.init(Cipher.DECRYPT_MODE, key, iv);

        final byte[] plainText = decipher.doFinal(message);
        return new String(plainText, CHARSET_UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * generate keyBytes
     *
     * @param sKey origin privateKey
     * @return
     * @throws Exception
     */
    private static byte[] getKeyBytes(String sKey) throws Exception {
        final MessageDigest md = MessageDigest.getInstance(ALGORITHM_MD5);
        final byte[] digestOfPassword = md.digest(sKey.getBytes(CHARSET_UTF_8));
        final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
        for (int j = 0, k = 16; j < 8; ) {
            keyBytes[k++] = keyBytes[j++];
        }
        return keyBytes;
    }

    public static String getMD5(String inStr) {
        MessageDigest md5 = null;
        StringBuilder hexValue = new StringBuilder();
        try {
            md5 = MessageDigest.getInstance("MD5");


            byte[] byteArray = inStr.getBytes("UTF-8");
            byte[] md5Bytes = md5.digest(byteArray);
            for (int i = 0; i < md5Bytes.length; i++) {
                int val = ((int) md5Bytes[i]) & 0xff;
                if (val < 16) {
                    hexValue.append("0");
                }
                hexValue.append(Integer.toHexString(val));
            }
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            throw new RuntimeException("加密异常");
        }
        return hexValue.toString();
    }
    
    public static String getMD5(String inStr,String in) {
        return getMD5(getMD5(getMD5(inStr) + getMD5(in)));
    }

    /**
     * data[]进行编码
     *
     * @param data
     * @return
     */
    public static String encode(byte[] data) {
        int start = 0;
        int len = data.length;
        StringBuffer buf = new StringBuffer();

        int end = len;
        int i = start;

        while (i < end) {
            int d = ((int) data[i]) & 0x0ff;
            buf.append(Integer.toHexString((d >> 4) & 15).toUpperCase());
            buf.append(Integer.toHexString(d & 15).toUpperCase());

            i ++;
        }


        return buf.toString();
    }

    private static int decode(char c) {
        if (c >= 'A' && c <= 'Z')
            return ((int) c) - 65;
        else if (c >= 'a' && c <= 'z')
            return ((int) c) - 97 + 26;
        else if (c >= '0' && c <= '9')
            return ((int) c) - 48 + 26 + 26;
        else
            switch (c) {
                case '+':
                    return 62;
                case '/':
                    return 63;
                case '=':
                    return 0;
                default:
                    throw new RuntimeException("unexpected code: " + c);
            }
    }

    /**
     * Decodes the given Base64 encoded String to a new byte array. The byte
     * array holding the decoded data is returned.
     */

    public static byte[] decode(String s) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            decode(s, bos);
        } catch (IOException e) {
            throw new RuntimeException();
        }
        byte[] decodedBytes = bos.toByteArray();
        try {
            bos.close();
            bos = null;
        } catch (IOException ex) {
            System.err.println("Error while decoding BASE64: " + ex.toString());
        }
        return decodedBytes;
    }

    private static void decode(String s, OutputStream os) throws IOException {
        int i = 0;
        int len = s.length() / 2;

        while (i < len) {
            int tri = Integer.parseInt(s.substring(2 * i, 2 * i + 2),16) & 0xff;
            if (tri > 127) {
                tri = tri -256;
            }
            os.write((short)tri);
            i ++;
        }
    }
    
    
    public static String encrypt(String str) {
        if (null == str) {
            return "";
        }
        return encode(encrypt(str, privateKey));
    }

    public static String decrypt(String str) {
        return decrypt(decode(str), privateKey);
    }
}
